import time
import os
import asyncio

from .const import *
from .tool import _tryParseJson

# 一些常用的中间件

def json(limit = 15 * MB, timeout = 20):
    '''
        用于返回一个处理json的中间件函数
        limit 参数默认 请求体大小不超过 15M
    '''
    async def func(req, res, next):
        # 先判断请求头类型是不是 application/json
        header_result = req.getHeader('content-type')
        for i in header_result:
            if 'application/json' in i:
                break
        else:
            # 如果不包含 json 请求头，直接进入下一个中间件
            next()
            return 
        # 记录进入中间件的时间
        startTime = time.time()
        # 将接收缓冲区的所有字符存储到 body里
        body = await req.getBody(size=limit)
        while True:
            try:
                # 先判断是否超时
                if time.time() - startTime > timeout:
                    break
                # 判断接收长度是否超出限制
                if len(body) > limit:
                    body = body[:limit]
                # 尝试将现有数据解析为 json
                jsonData = _tryParseJson(body.decode('utf-8'))
                # 解析成功则直接返回
                if jsonData:
                    req.body = jsonData
                    break
                # 如果超出限制，也直接返回
                if len(body) == limit:
                    break
                # 否则继续接收数据
                data = req.getBody(size=limit)
                body += data
            except BlockingIOError as e:
                await asyncio.sleep(0)
        if req.body is None:
            req.body = {}
        next()
    return func

def urlencoded(limit = 8 * KB):
    '''返回一个中间件用于解析URL编码格式的请求体'''
    def func(req, res, next):
        if req.query is not None:
            query = req.query.split('&')
            result = {}
            for i in query:
                if '=' in i:
                    key, value = i.split('=')
                    result[key] = value
                else:
                    result[i] = ''
            req.query = result
        next()
    return func

def static(abspath):
    '''
        返回一个中间件用于提供静态文件
        此中间件暂时以传入绝对路径为准

        因为无法避免相对路径导致的一些未知错误
        关于后续是否支持传入相对路径之后再定
    '''
    def func(req, res, next):
        """
            其实路径拼接这段逻辑应该增加异常处理的
            先不管 后续添加
        """
        # 获取请求路径
        filepath = req.path
        # 去除开头的 /
        if filepath[0] == '/':
            filepath = filepath[1:]
        # 规范化路径 -- 这次的规范化是为了更好拼接路径
        filepath = os.path.normpath(filepath)
        # 拼接路径
        filepath = os.path.join(abspath, filepath)
        # 再次进行规范化路径 避免越权漏洞
        filepath = os.path.normpath(filepath)
        # 检查路径是否在允许的目录内
        if not filepath.startswith(abspath):
            next()
            return
        # 尝试打开文件
        try:
            with open(filepath, 'rb') as f:
                # 这里后续需要处理成分片异步发送
                data = f.read()
                res.send(data)
        except FileNotFoundError as e:
            next()
            return
    return func

def cookie():
    ''' 用于解析 cookie 的中间件 '''
    ...
